#!/bin/bash

umask 0077

declare -i ret_ok=0                             # 成功
declare -i ret_failed_param_wrong=1             # 失败 参数错误
declare -i ret_failed_crl_format_check=2        # 失败 CRL文件格式错误
declare -i ret_failed_crl_update_time_check=3   # 失败 update时间异常
declare -i ret_failed_crl_compare_update_time=4 # 失败 新文件比老文件的更新时间还要早
declare -i ret_failed_crl_update=5              # 失败 CRL文件更新失败
declare -i ret_failed_internal_error=6          # 失败 内部错误

WORK_PATH="/opt/middleware/AtlasEdge"
SCRIPT_PATH="/opt/middleware/AtlasEdge/software/edge_site/edge_manager/src/script"
ACTIVE=""
CRL_PATH=""
FORCE_UPDATE=""
OPENSSL_APP="openssl"
CA_FILE="FD.crt"
CRL_FILE="FDCrl.crl"
CRL_FILE_BAK="FDCrl.crl.bak"
EDGE_MAIN_APP="edge_main.sh"

. ${SCRIPT_PATH}/log_util.sh
. ${SCRIPT_PATH}/common.sh

function prepare()
{
    if ! type $OPENSSL_APP > /dev/null 2>&1; then
        logger_Warn "$OPENSSL_APP not found"
        return $ret_failed_internal_error
    fi

    CRL_DIR=$(get_cert_dir)
    CRL_FILE="$CRL_DIR/$CRL_FILE"
    CRL_FILE_BAK="$CRL_DIR/$CRL_FILE_BAK"
    CA_FILE="$CRL_DIR/$CA_FILE"

    if [ ! -f "$CA_FILE" ]; then
        logger_Warn "$(basename $CA_FILE) not exists"
        return $ret_failed_internal_error
    fi

    crl_file_checksum=$(sha256sum $CRL_FILE 2>/dev/null | awk '{print $1}')
    crl_path_checksum=$(sha256sum $CRL_PATH 2>/dev/null | awk '{print $1}')

    if [ "$crl_file_checksum" == "$crl_path_checksum" ]; then
        logger_Warn "$(basename $CRL_FILE) and $(basename $CRL_PATH) are identical"
        return $ret_failed_internal_error
    fi

    logger_Info "       ACTIVE=$ACTIVE"
    logger_Info "     CRL_PATH=$(basename $CRL_PATH)"
    logger_Info " FORCE_UPDATE=$FORCE_UPDATE"
    logger_Info "      CA_FILE=$(basename $CA_FILE)"
    logger_Info "     CRL_FILE=$(basename $CRL_FILE)"
    logger_Info " CRL_FILE_BAK=$(basename $CRL_FILE_BAK)"

    return $ret_ok
}

function exit_trap()
{
    trap - INT TERM EXIT QUIT ABRT
    logger_Info "updateCRL exit pid: $$"
}

function verify_parameters()
{
    if [[ -z "$CRL_PATH" || -z "$FORCE_UPDATE" || -z "$ACTIVE" ]]; then
        logger_Error "some parameter is missing: crlPath=$CRL_PATH, forceupdate=$FORCE_UPDATE, active=$ACTIVE"
        return $ret_failed_param_wrong
    fi

    if [ ! -f "$CRL_PATH" ]; then
        logger_Error "crlPath $(basename $CRL_PATH) not exists"
        return $ret_failed_param_wrong
    fi

    if [[ "$FORCE_UPDATE" != "true" && "$FORCE_UPDATE" != "false" ]]; then
        logger_Error "unknown forceupdate $FORCE_UPDATE, allowed true or false"
        return $ret_failed_param_wrong
    fi

    if [[ "$ACTIVE" != "true" && "$ACTIVE" != "false" ]]; then
        logger_Error "unknown active $ACTIVE, allowed true or false"
        return $ret_failed_param_wrong
    fi

    return $ret_ok
}

function verify_crl_format()
{
    output=$($OPENSSL_APP crl -in $CRL_PATH -inform DER -CAfile $CA_FILE -noout 2>&1)
    if [ "$?" -eq 0 ]; then
        logger_Info "crl format check success"
        return $ret_ok
    else
        logger_Warn "crl format check failed: $output"
        return $ret_failed_crl_format_check
    fi
}

function get_update_time_by_type()
{
    crl_file=$1
    time_type=$2
    crl_time=$($OPENSSL_APP crl -inform DER -in $crl_file -noout -$time_type | awk -F '=' '{print $2}')
    epoch_seconds=$(date --date="$crl_time" +"%s")
    echo $epoch_seconds
}

function verify_crl_update_time()
{
    current_date=$(date +%s)
    lastupdate=$(get_update_time_by_type "$CRL_PATH" "lastupdate")
    nextupdate=$(get_update_time_by_type "$CRL_PATH" "nextupdate")

    logger_Info "lastupdate=$lastupdate, nextupdate=$nextupdate, current_date=$current_date"

    if [[ "$current_date" -ge "$lastupdate" && "$current_date" -le "$nextupdate" ]]; then
        logger_Info "verify crl self update time success"
        return $ret_ok
    fi

    logger_Warn "verify crl self update time failed"
    return $ret_failed_crl_update_time_check
}

function compare_crl_update_time()
{
    old_crl_file=$1
    new_crl_file=$2

    if [ ! -f "$old_crl_file" ]; then
        logger_Warn "old crl file not exists"
        return $ret_ok
    fi

    logger_Info "compare $(basename $old_crl_file) with $(basename $new_crl_file)"

    old_crl_last_update=$(get_update_time_by_type "$old_crl_file" "lastupdate")
    new_crl_last_update=$(get_update_time_by_type "$new_crl_file" "lastupdate")
    logger_Info "new_crl_last_update=$new_crl_last_update, old_crl_last_update=$old_crl_last_update"

    if [ "$new_crl_last_update" -lt "$old_crl_last_update" ]; then
        logger_Warn "compare crl update time failed"
        return $ret_failed_crl_compare_update_time
    fi

    logger_Info "compare crl update time success"
    return $ret_ok
}

function update_crl_file()
{
    old_crl_file=$CRL_FILE
    new_crl_file=$CRL_PATH

    compare_crl_update_time "$old_crl_file" "$new_crl_file"
    local ret=$?
    if [[ "$ret" -ne "$ret_ok" && "$FORCE_UPDATE" == "false" ]]; then
        logger_Warn "compare crl time return $ret, forceupdate not set, don't update"
        return $ret_failed_crl_compare_update_time
    fi

    local ret1=0
    if [ -f "$old_crl_file" ]; then
        mv -f $old_crl_file $CRL_FILE_BAK
        ret1=$?
    fi

    chmod 600 $new_crl_file
    mv -f $new_crl_file $old_crl_file
    local ret2=$?

    if [[ $ret1 -ne 0 || $ret2 -ne 0 ]]; then
        logger_Warn "update crl file failed"
        return $ret_failed_crl_update
    fi

    logger_Info "update crl file done"
    return $ret_ok
}

function get_user_confirm()
{
    local timeout=30
    local restart_service=0
    local prompt="Active CRL will restart edge core service, are you sure want to continue (yes/no)? "

    read -t $timeout -p "$prompt" yn
    if [ "$?" -ne 0 ]; then
        logger_Info "read user input timeout"
        return $restart_service
    fi

    case $yn in
        yes)
            restart_service=1
            ;;
        no)
            restart_service=0
            ;;
        *)
            logger_Warn "invalid answer"
            ;;
    esac

    logger_Info "user confirm $restart_service"
    return $restart_service
}

function apply_crl_file()
{
    if [ "$ACTIVE" != "true" ]; then
        logger_Info "active flag not set, skip apply crl file"
        return $ret_ok
    fi

    get_user_confirm
    if [ "$?" -eq 0 ]; then
        logger_Info "user canceled, skip apply crl file"
        return $ret_ok
    fi

    logger_Info "stop atlasedge service"
    bash ${WORK_PATH}/run.sh stop

    logger_Info "start atlasedge service"
    bash ${WORK_PATH}/run.sh start

    logger_Info "apply crl file done"
    return $ret_ok
}

function update_core_config_file()
{
    default_crl_path="/etc/pki/tls/fd.crl"
    core_config_file="${WORK_PATH}/software/edge_core/conf/edgecore.yaml"

    sed -i "s|$default_crl_path|$CRL_FILE|g" $core_config_file

    grep $CRL_FILE $core_config_file > /dev/null 2>&1
    if [ "$?" -ne 0 ]; then
        logger_Warn "update core config file failed"
    else
        logger_Info "update core config file success"
    fi

    return $ret_ok
}

function usage()
{
    echo "updateCRL -- update CRL file"
    echo "parameters:"
    echo "    --crlPath             crl file to update"
    echo "    --forceupdate         force update will ignore error checking with the exist CRL file"
    echo "    --active              active set to true will restart edge main to take effect"
    echo "    -h, --help"
    echo "usage:"
    echo "    ./updateCRL.sh --crlPath=/tmp/newCrl.crl --forceupdate=true --active=true"
}

function update_crl()
{
    declare -a proceed_list=(verify_parameters
        prepare
        verify_crl_format
        verify_crl_update_time
        update_crl_file
        update_core_config_file
        apply_crl_file)

    func_cnt=${#proceed_list[@]}

    for ((i = 0; i < func_cnt; i++)); do
        local func=${proceed_list[$i]}
        logger_Info "update crl invoke $((i + 1))/$func_cnt: $func"
        $func
        ret=$?
        if [ "$ret" != "$ret_ok" ]; then
            logger_Warn "$func failed, ret is $ret, exit crl update"
            return $ret
        fi
    done

    logger_Info "update crl finished"
    return $ret_ok
}

function main()
{
    trap exit_trap INT TERM EXIT QUIT ABRT
    logger_Info "updateCRL begin pid: $$, cmd params: ${*}"
    ARGS=$(getopt -o h -a -l crlPath:,forceupdate:,active:,help -n "updateCRL.sh" -- "$@")
    if [ $? != 0 ]; then
        logger_Warn "Terminating....." >&2
        exit $ret_failed_param_wrong
    fi

    eval set -- "$ARGS"

    while true; do
        case $1 in
            --crlPath)
                CRL_PATH="$2"
                ;;
            --forceupdate)
                FORCE_UPDATE=$2
                ;;
            --active)
                ACTIVE=$2
                ;;
            --help | -h)
                usage
                exit $ret_failed_param_wrong
                ;;
            --)
                shift
                break
                ;;
            *)
                logger_Warn "unkown argument: $OPTARG args:$@"
                exit $ret_failed_param_wrong
                ;;
        esac
        shift 2
    done

    update_crl
    return $?
}

main "$@"
RESULT=$?
exit $RESULT
